package com.hd123.rumba.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.apache.log4j.Logger;

/**
 * 通过修改web.xml文件的时间, 促使tomcat重新装载此应用
 * <pre>
 *   &lt;servlet&gt;
 *     &lt;servlet-name&gt;SdpmDaemon&lt;/servlet-name&gt;
 *     &lt;servlet-class&gt;com.hd123.rumba.servlet.Reload&lt;/servlet-class&gt;
 *     &lt;init-param&gt;
 *         &lt;param-name&gt;reload&lt;/param-name&gt;
 *         &lt;param-value&gt;5:00&lt;/param-value&gt;
 *     &lt;/init-param&gt; 
 *     &lt;load-on-startup&gt;200&lt;/load-on-startup&gt; 
 *   &lt;/servlet&gt;
 * </pre>
 */
public class Reload extends HttpServlet {

  public void init(ServletConfig sc) throws ServletException {
    Logger logger = Logger.getLogger(this.getClass());

    ReloadTask reloadTask = new ReloadTask(sc.getServletContext().getRealPath("WEB-INF"));
    Calendar cal = Calendar.getInstance();
    cal.setTime(new Date());
    cal.set(Calendar.HOUR_OF_DAY, 5);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    try {
      String time = sc.getInitParameter("reload");
      int i = time.indexOf(':');
      if (i > 0) {
        int h = Integer.parseInt(time.substring(0, i));
        int m = Integer.parseInt(time.substring(i + 1));
        if (h >= 0 && h < 24 && m >= 0 && m < 60) {
          cal.set(Calendar.HOUR_OF_DAY, h);
          cal.set(Calendar.MINUTE, m);
        }
      }
    } catch (Exception e) {
    }
    Date st = cal.getTime();
    if (st.before(new Date()))
      cal.add(Calendar.DATE, 1);
    Timer timer = new Timer();
    timer.schedule(reloadTask, cal.getTime());
    logger.info("Reload is scheduled at "
        + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(cal.getTime()));
  }

  public static class ReloadTask extends TimerTask {
    String homePath;
    Logger logger = Logger.getLogger(this.getClass());

    public ReloadTask(String homePath) {
      super();
      this.homePath = homePath;
    }

    public void run() {
      // 修改WEB-INF/web.xml文件的时间, 使得此应用被tomcat重新装载.
      // 如果不存在web$x.xml, 则将web.xml复制到web$x.xml, 其中,
      // x是web.xml的文件时间yyyyMMddHHmmss.
      // 否则, 保留最新的web$x.xml, 比较它们的内容. 如果两者的内容不一致, 则删除web$x.xml, 重新创建web$x.xml
      // copy web$x.xml to web.xml以修改它的时间.
      // 这样, web$x.xml的x表达了此web.xml的人工修改时间.
      logger.info("reloading...");
      try {
        String webinf = homePath;
        File dir = new File(webinf);
        File webxml = new File(webinf, "web.xml");

        // 保留最新的web$x.xml
        File[] files = dir.listFiles();
        Vector<File> backups = new Vector();
        for (int i = 0; i < files.length; ++i) {
          if (files[i].getName().matches("web\\$(.)*\\.xml"))
            backups.add(files[i]);
        }
        if (backups.size() > 1) {
          Collections.sort(backups);
          while (backups.size() > 1) {
            backups.get(0).delete();
            backups.remove(0);
          }
        }

        File backup = null;
        if (backups.size() > 0)
          backup = backups.get(0);
        if (backup != null) {
          if (!compareFile(webxml, backup)) {
            backup.delete();
            backup = null;
          }
        }
        if (backup == null) {
          backup = new File(webinf, "web$"
              + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(webxml.lastModified()))
              + ".xml");
          copyFile(webxml, backup);
        }
        copyFile(backup, webxml);

      } catch (IOException e) {
        logger.error(e);
      }
    }

    public void copyFile(File inFile, File outFile) throws IOException {
      FileInputStream ins = new FileInputStream(inFile);
      FileOutputStream out = new FileOutputStream(outFile);
      byte abyte0[] = new byte[2048];
      int bytesRead, len = 0;
      while (-1 != (bytesRead = ins.read(abyte0, 0, abyte0.length))) {
        len += bytesRead;
        out.write(abyte0, 0, bytesRead);
      }
      ins.close();
      out.close();
    }

    public boolean compareFile(File a, File b) throws IOException {
      if (a == null || b == null)
        return true;
      if (a.length() != b.length())
        return true;
      FileInputStream as = new FileInputStream(a);
      FileInputStream bs = new FileInputStream(b);
      byte abytes[] = new byte[2048];
      byte bbytes[] = new byte[2048];
      int bytesRead;
      boolean isSame = false;
      while (-1 != (bytesRead = as.read(abytes, 0, abytes.length))) {
        bs.read(bbytes, 0, bbytes.length);
        isSame = compareBytes(abytes, bbytes, bytesRead);
        if (!isSame)
          break;
      }
      as.close();
      bs.close();
      return isSame;
    }

    public boolean compareBytes(byte[] a, byte[] b, int len) {
      if (a.length < len || b.length < len)
        return false;
      for (int i = 0; i < len; ++i)
        if (a[i] != b[i])
          return false;
      return true;
    }
  }
}
